# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

load("@rules_cc//cc/toolchains:args.bzl", "cc_args")
load("@rules_cc//cc/toolchains:args_list.bzl", "cc_args_list")
load("@rules_cc//cc/toolchains:feature.bzl", "cc_feature")

package(default_visibility = ["//visibility:public"])

cc_args(
    name = "empty_cc_args",
    actions = [],
)

label_flag(
    name = "extra_toolchain_args",
    build_setting_default = ":empty_cc_args",
    # This is an injection point used by Pigweed's toolchains.
    # While anyone should be allowed to set this flag, only Pigweed's
    # toolchains should be able to read it.
    visibility = ["//pw_toolchain:__subpackages__"],
)

# The common set of warnings for clang and arm_gcc.
cc_args(
    name = "common_warnings",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
    ],
    args = [
        "-Wall",
        "-Wextra",
        "-Wimplicit-fallthrough",
        "-Wcast-qual",
        "-Wpointer-arith",
        "-Wshadow",
        "-Wredundant-decls",
        # TODO: b/259746255 - Enable implicit conversion warnings once fixed.
        # "-Wconversion",
        # Make all warnings errors, except for the exemptions below.
        "-Werror",
        "-Wno-error=cpp",  # preprocessor #warning statement
        "-Wno-error=deprecated-declarations",  # [[deprecated]] attribute
    ],
)

# This config contains warnings that are enabled for upstream Pigweed.
# This config MUST NOT ever be exposed to downstream.
cc_args_list(
    name = "internal_strict_warnings",
    args = [
        ":pedantic_warnings",
        ":extra_pigweed_warnings",
    ],
    visibility = ["//:__subpackages__"],
)

cc_args(
    name = "extra_pigweed_warnings",
    actions = [
        "@rules_cc//cc/toolchains/actions:compile_actions",
    ],
    args = [
        "-Wswitch-enum",
        "-Wextra-semi",
    ],
    visibility = ["//:__subpackages__"],
)

cc_args(
    name = "pedantic_warnings",
    actions = [
        "@rules_cc//cc/toolchains/actions:compile_actions",
    ],
    args = [
        # Enable -Wpedantic, but disable a few warnings.
        "-Wpedantic",

        # Allow designated initializers, which were added in C++20 but widely
        # supported prior and permitted by the Google style guide.
        "-Wno-c++20-designator",

        # Allow empty ... arguments in macros, which are permitted in C++20 but
        # widely supported prior.
        "-Wno-gnu-zero-variadic-macro-arguments",
    ],
)

cc_args(
    name = "w_conversion",
    actions = ["@rules_cc//cc/toolchains/actions:cpp_compile_actions"],
    args = ["-Wconversion"],
)

# This is currently expressed as a feature so it can be selectively disabled
# while fixes are rolled out.
cc_feature(
    name = "conversion_warnings_feature",
    args = [":w_conversion"],
    feature_name = "conversion_warnings",
    visibility = ["//:__subpackages__"],
)

cc_args(
    name = "w_ctad_maybe_unsupported",
    actions = ["@rules_cc//cc/toolchains/actions:cpp_compile_actions"],
    args = ["-Wctad-maybe-unsupported"],
)

# This is currently expressed as a feature so it can be selectively disabled
# while fixes are rolled out.
# TODO: https://pwbug.dev/417734962 - Eventually this should be added to
# internal_strict_warnings.
cc_feature(
    name = "ctad_warnings_feature",
    args = [":w_ctad_maybe_unsupported"],
    feature_name = "ctad_warnings",
    visibility = ["//:__subpackages__"],
)

# Allow uses of the register keyword, which may appear in C headers.
cc_args(
    name = "wno_register",
    actions = ["@rules_cc//cc/toolchains/actions:cpp_compile_actions"],
    args = ["-Wno-register"],
)

# Issue a warning when a class appears to be polymorphic, yet it declares a
# non-virtual destructor
cc_args(
    name = "wnon_virtual_dtor",
    actions = ["@rules_cc//cc/toolchains/actions:cpp_compile_actions"],
    args = ["-Wnon-virtual-dtor"],
)

# Prevent relative paths from being converted to absolute paths.
cc_args(
    name = "no_canonical_prefixes",
    actions = [
        "@rules_cc//cc/toolchains/actions:compile_actions",
    ],
    args = ["-no-canonical-prefixes"],
)

# Enables colors in compiler diagnostics. This uses the GCC spelling of the
# flag, which Clang supports as an undocumented extension.
cc_args(
    name = "color_diagnostics",
    actions = [
        "@rules_cc//cc/toolchains/actions:assembly_actions",
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = ["-fdiagnostics-color"],
)

cc_args(
    name = "debugging",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
    ],
    args = ["-g"],
)

# Compile without runtime type information (RTTI). This produces smaller binaries.
cc_args(
    name = "no_rtti",
    actions = ["@rules_cc//cc/toolchains/actions:cpp_compile_actions"],
    args = ["-fno-rtti"],
)

# Optimization level option
cc_args(
    name = "o2",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = ["-O2"],
)

# Optimization aggressively for size rather than speed option
cc_args(
    name = "oz",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = ["-Oz"],
)

# Standard compiler flags to reduce output binary size.
cc_args(
    name = "reduced_size",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fno-common",
        "-fno-exceptions",
        "-ffunction-sections",
        "-fdata-sections",
    ],
)

cc_feature(
    name = "c++17_feature",
    args = [":c++17"],
    feature_name = "c++17",
)

# Compile for the C++17 standard.
cc_args(
    name = "c++17",
    actions = [
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = ["-std=c++17"],
)

cc_feature(
    name = "c++20_feature",
    args = [":c++20"],
    feature_name = "c++20",
)

# Compile for the C++20 standard.
cc_args(
    name = "c++20",
    actions = [
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = ["-std=c++20"],
)

cc_args(
    name = "asan",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fsanitize=address",
        "-DADDRESS_SANITIZER",
        "-fno-sanitize-recover=all",
    ],
)

# TODO: https://pwbug.dev/346388161 - Push this to upstream rules_cc.
cc_args(
    name = "msan",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fsanitize=memory",
        "-DMEMORY_SANITIZER",
        "-fno-sanitize-recover=all",
    ],
)

# TODO: https://pwbug.dev/346388161 - Push this to upstream rules_cc.
cc_args(
    name = "ubsan",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fsanitize=undefined",
        "-DUNDEFINED_SANITIZER",
        "-fno-sanitize-recover=all",
    ],
)

# TODO: https://pwbug.dev/346388161 - Push this to upstream rules_cc.
cc_args(
    name = "tsan",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fsanitize=thread",
        "-DTHREAD_SANITIZER",
        "-fno-sanitize-recover=all",
    ],
)

# Thread safety warnings are only supported by Clang.
cc_args(
    name = "thread_safety_warnings",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
    ],
    args = [
        "-Wthread-safety",
        "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1",
    ],
)

cc_args(
    name = "fuzztest",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:link_actions",
    ],
    args = [
        "-fsanitize-coverage=inline-8bit-counters,trace-cmp,pc-table",
        "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION",
        "-UNDEBUG",
        "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG",
    ] + select({
        "@platforms//cpu:x86_64": ["-mcrc32"],
        "//conditions:default": [],
    }),
)

cc_args(
    name = "layering_check",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_header_parsing",
        "@rules_cc//cc/toolchains/actions:cpp_module_compile",
    ],
    args = [
        "-fmodules-strict-decluse",
        "-Wprivate-header",
    ],
)

cc_args(
    name = "module_name",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_header_parsing",
        "@rules_cc//cc/toolchains/actions:cpp_module_compile",
    ],
    args = select({
        "@platforms//os:macos": [
            "-Xclang",
        ],
        "//conditions:default": [],
    }) + ["-fmodule-name={module_name}"],
    format = {
        "module_name": "@rules_cc//cc/toolchains/variables:module_name",
    },
    requires_not_none = "@rules_cc//cc/toolchains/variables:module_name",
)

cc_args(
    name = "module_map_file",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_header_parsing",
        "@rules_cc//cc/toolchains/actions:cpp_module_compile",
    ],
    args = select({
        "@platforms//os:macos": [
            "-Xclang",
        ],
        "//conditions:default": [],
    }) + ["-fmodule-map-file={module_map_file}"],
    format = {
        "module_map_file": "@rules_cc//cc/toolchains/variables:module_map_file",
    },
    requires_not_none = "@rules_cc//cc/toolchains/variables:module_map_file",
)

cc_args(
    name = "dependent_module_map_files",
    actions = [
        "@rules_cc//cc/toolchains/actions:c_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
        "@rules_cc//cc/toolchains/actions:cpp_header_parsing",
        "@rules_cc//cc/toolchains/actions:cpp_module_compile",
    ],
    args = select({
        "@platforms//os:macos": ["-Xclang"],
        "//conditions:default": [],
    }) + [
        "-fmodule-map-file={dependent_module_map_files}",
    ],
    format = {
        "dependent_module_map_files": "@rules_cc//cc/toolchains/variables:dependent_module_map_files",
    },
    iterate_over = "@rules_cc//cc/toolchains/variables:dependent_module_map_files",
    requires_not_none = "@rules_cc//cc/toolchains/variables:dependent_module_map_files",
)
